divert(`-1')
include(`macros.m4')

dnl
define(`_R',  `r`'eval($1+($2/2))')

dnl: $1, Index
dnl: $2, Stride
dnl: $3, Offset
define(`_RA', `eval($1*$2+$3)')
define(`_RO', `eval(_RA($1,$2,$3)%2)')

dnl: $1, Base
dnl: $2, Index
dnl: $3, Stride
dnl: $4, Offset
define(`_RI', `eval($1+_RA($2,$3,$4)/2)')
define(`_RS', `r`'_RI($1,$2,$3,$4)')

define(`MUL_CPX_XY', `dnl: input $1 $2 $3; tmp $4 $5
    dnl: $1, In/Out reg, i.e. r100
    dnl: $2, oprand 2, i.e. l1.xzxz
    dnl: $3, coeff, i.e. l1.wwww
    dnl: 
    dnl: $4, $5, temp register, i.e. r120, r121

; Begin Complex Multiply XY
ifelse($3, `0', `    mov $4.xy__, $2', `    mul_ieee $4.xy__, $2, $3')

    mul_ieee $5.xy__, $1.xyxy, $4.xyxy
    mul_ieee $5.__zw, $1.xyxy, $4.yxyx
    sub $1.x___, $5.x, $5.y
    add $1._y__, $5.z, $5.w
; End Complex Multiply XY')

define(`MUL_CPX_ZW', `dnl: input $1 $2 $3; tmp $4 $5
    dnl: $1, In/Out reg, i.e. r100
    dnl: $2, oprand 2, i.e. l1.xzxz
    dnl: $3, coeff, i.e. l1.wwww
    dnl: 
    dnl: $4, $5, temp register, i.e. r120, r121

; Begin Complex Multiply ZW
ifelse($3, `0', `    mov $4.__zw, $2', `    mul_ieee $4.__zw, $2, $3')

    mul_ieee $5.xy__, $1.zwzw, $4.zwzw
    mul_ieee $5.__zw, $1.zwzw, $4.wzwz
    sub $1.__z_, $5.x, $5.y
    add $1.___w, $5.z, $5.w
; End Complex Multiply ZW')

define(`MUL_CPX', `dnl: input $1 $2 $3 $4; tmp $5, $6
    dnl: $1, Base,   i.e. 100
    dnl: $2, Offset, i.e. 3 means r101.zw
    dnl: $3, oprand 2, i.e. l1.xzxz
    dnl: $4, coeff, i.e. l1.wwww
    dnl:
    dnl: $5, $6, temp register, i.e. r100, r101
ifelse(eval($2&1), `0', `MUL_CPX_XY(_R($1,$2), $3, $4, $5, $6)',
       eval($2&1), `1', `MUL_CPX_ZW(_R($1,$2), $3, $4, $5, $6)',
`__MUL_CPX_INVALID__')')

define(`FFT_2_XYZW', `dnl input $1; temp $2 
; Begin FFT_2 XYZW
    mov $2.xy__, $1.xyzw
    add $1.xy__, $2.xyxy, $1.zwzw
    sub $1.__zw, $2.xyxy, $1.zwzw
; End FFT_2 Reg')

define(`FFT_2_XY', `dnl input $1 $2; temp $3 
; Begin FFT_2 XY
    mov $3.xy__, $1.xyxy
    add $1.xy__, $3.xyxy, $2.xyxy
    sub $2.xy__, $3.xyxy, $2.xyxy
; End FFT_2 XY')

define(`FFT_2_ZW', `dnl input $1 $2; temp $3 
; Begin FFT_2 ZW
    mov $3.__zw, $1.zwzw
    add $1.__zw, $3.zwzw, $2.zwzw
    sub $2.__zw, $3.zwzw, $2.zwzw
; End FFT_2 ZW')

define(`FFT_2_ARRAY', `dnl input $1 $2 $3; tmp $4
    dnl $1, Base reg number, i.e. 100
    dnl $2, Stride in 2 components
    dnl $3, Offset, 0 means XY, 1 means ZW, others invalid
    dnl 
    dnl $4, temp register

ifelse($2, `1', `FFT_2_XYZW(_R($1,0), $4)', 
       $3, `0', `FFT_2_XY(_RS($1,0,$2,$3), _RS($1,1,$2,$3), $4)',
       $3, `1', `FFT_2_ZW(_RS($1,0,$2,$3), _RS($1,1,$2,$3), $4)',
`__FFT_2_INVALID__')')
   
define(`FFT_4_ARRAY', `dnl: input $1 $2 $3; temp $4 $5
    dnl $1, Base reg number, i.e. 100
    dnl $2, Stride in 2 components
    dnl $3, Offset
    dnl 
    dnl $4, $5, temp register

; Begin FFT_4
    FFT_2_ARRAY(_RI($1,0,$2,$3), eval(2*$2), _RO(0,$2,$3), $4)
    FFT_2_ARRAY(_RI($1,1,$2,$3), eval(2*$2), _RO(1,$2,$3), $4)

    MUL_CPX($1, _RA(3,$2,$3), l1.xzxz, 0, $4, $5)

    FFT_2_ARRAY(_RI($1,0,$2,$3), eval(1*$2), _RO(0,$2,$3), $4)
    FFT_2_ARRAY(_RI($1,2,$2,$3), eval(1*$2), _RO(2,$2,$3), $4)

; End FFT_4')


define(`FFT_8_ARRAY', `dnl: input $1 $2 $3; temp $4 $5
    dnl $1, Base reg number, i.e. 100
    dnl $2, Stride in 2 components
    dnl $3, Offset
    dnl 
    dnl $4, $5, temp register

; Begin FFT_8
generalForLoop(`ID', 0, 3, +1, `dnl
    FFT_2_ARRAY(_RI($1,ID,$2,$3), eval(4*$2), _RO(ID,$2,$3), $4)
')

    MUL_CPX($1, _RA(5,$2,$3), l1.yzyz, l1.wwww, $4, $5)
    MUL_CPX($1, _RA(6,$2,$3), l1.xzxz,       0, $4, $5)
    MUL_CPX($1, _RA(7,$2,$3), l1.zzzz, l1.wwww, $4, $5)

generalForLoop(`ID', 0, 1, +1, `dnl
    FFT_4_ARRAY(_RI($1,eval(4*ID),$2,$3), eval($2), _RO(eval(4*ID),$2,$3), $4, $5)
')
; End FFT_8
')

define(`FFT_16_ARRAY', `dnl: input $1 $2 $3; temp $4 $5
    dnl $1, Base reg number, i.e. 100
    dnl $2, Stride in 2 components
    dnl $3, Offset
    dnl 
    dnl $4, $5, temp register

; Begin FFT_16

generalForLoop(`ID', 0, 3, +1, `dnl
    FFT_4_ARRAY(_RI($1,ID,$2,$3), eval(4*$2), _RO(ID,$2,$3), $4, $5)
')

    MUL_CPX($1, _RA( 5,$2,$3), l1.yzyz, l1.wwww, $4, $5)
    MUL_CPX($1, _RA( 6,$2,$3), l1.xzxz,       0, $4, $5)
    MUL_CPX($1, _RA( 7,$2,$3), l1.zzzz, l1.wwww, $4, $5)
    MUL_CPX($1, _RA( 9,$2,$3), l2.yzyz,       0, $4, $5)
    MUL_CPX($1, _RA(10,$2,$3), l1.yzyz, l1.wwww, $4, $5)
    MUL_CPX($1, _RA(11,$2,$3), l2.xwxw,       0, $4, $5)
    MUL_CPX($1, _RA(13,$2,$3), l2.xwxw,       0, $4, $5)
    MUL_CPX($1, _RA(14,$2,$3), l1.zzzz, l1.wwww, $4, $5)
    MUL_CPX($1, _RA(15,$2,$3), l2.wxwx,       0, $4, $5)

generalForLoop(`ID', 0, 3, +1, `dnl
    FFT_4_ARRAY(_RI($1,eval(4*ID),$2,$3), eval($2), _RO(eval(4*ID),$2,$3), $4, $5)
')

; End FFT_16
')

define(`FFT_32_ARRAY', `dnl: input $1 $2 $3; temp $4 $5
    dnl $1, Base reg number, i.e. 100
    dnl $2, Stride in 2 components
    dnl $3, Offset
    dnl 
    dnl $4, $5, temp register

; Begin FFT_32

generalForLoop(`ID', 0, 7, +1, `dnl
    FFT_4_ARRAY(_RI($1,ID,$2,$3), eval(8*$2), _RO(ID,$2,$3), $4, $5)
')

    MUL_CPX($1, _RA( 9,$2,$3), l10.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(10,$2,$3), l10.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(11,$2,$3), l11.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(12,$2,$3),  l1.xzxz, 0, $4, $5)
    MUL_CPX($1, _RA(13,$2,$3), l11.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(14,$2,$3), l12.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(15,$2,$3), l12.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(17,$2,$3), l13.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(18,$2,$3), l13.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(19,$2,$3), l14.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(20,$2,$3), l14.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(21,$2,$3), l15.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(22,$2,$3), l15.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(23,$2,$3), l16.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(25,$2,$3), l16.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(26,$2,$3), l17.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(27,$2,$3), l17.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(28,$2,$3), l18.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(29,$2,$3), l18.zwzw, 0, $4, $5)
    MUL_CPX($1, _RA(30,$2,$3), l19.xyxy, 0, $4, $5)
    MUL_CPX($1, _RA(31,$2,$3), l19.zwzw, 0, $4, $5)

generalForLoop(`ID', 0, 3, +1, `dnl
    FFT_8_ARRAY(_RI($1,eval(8*ID),$2,$3), eval($2), _RO(eval(8*ID),$2,$3), $4, $5)
')

; End FFT_32
')

define(`argn', `ifelse(`$1', 1, ``$2'',
`argn(decr(`$1'), shift(shift($@)))')')

define(`REV_BITS_4', `argn(incr($1), `0', `2', `1', `3')')

define(`REV_BITS_8', `argn(incr($1), 
    `0', `4', `2', `6', `1', `5', `3', `7')
')

define(`REV_BITS_16', `argn(incr($1), 
    `0', `8', `4', `12', `2', `10', `6', `14', 
    `1', `9', `5', `13', `4', `11', `7', `15')
')

define(`REV_BITS_32', `argn(incr($1), 
    `0', `16',  `8', `24', `4', `20', `12', `28', 
    `2', `18', `10', `26', `6', `22', `14', `30',
    `1', `17',  `9', `25', `5', `21', `13', `29',
    `3', `19', `11', `27', `7', `23', `15', `31')
')

divert
